package edu.mapi.ir.viewer;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.LinkedHashMap;
import java.util.Vector;

import javax.swing.JPanel;
/**
 * @author ZP
 */
public class MazePanel extends JPanel implements ComponentListener {
	
	private Maze maze;
	private Vector<MousePlayer> mice = new Vector<MousePlayer>();
	private LinkedHashMap<IMapLayer, BufferedImage> layers = new LinkedHashMap<IMapLayer, BufferedImage>();
	protected double zoom = 15;
	private boolean autoZoom = true;
	
	public void addLayer(IMapLayer mapLayer) {		
		layers.put(mapLayer, mapLayer.getLayerImage(zoom));
		updateLayers();
	}
	
	public void removeLayer(IMapLayer mapLayer) {
		layers.remove(mapLayer);		
	}
	
	public void updateLayers() {
		for (IMapLayer layer : layers.keySet()) {
			layers.put(layer, layer.getLayerImage(zoom));
		}
	}
	
	public void addMouse(MousePlayer mouse) {
		mice.add(mouse);
	}
	
	public void removeMouse(MousePlayer mouse) {
		mice.remove(mouse);
	}
	
	public void clearMice() {
		mice.clear();
		MousePlayer.num = 0;
	}
	
	public void drawMice(Graphics2D g) {
		
		for (MousePlayer m : mice) {
			g.setTransform(new AffineTransform());
			Point2D pt = getScreenCoordinates(m.getPosition());			
			g.translate(pt.getX(), pt.getY());
			
			g.rotate(-Math.toRadians(m.getDirection()-90));
			
			m.paintMouse(g, zoom);
		}
	}
	
	public void setMaze(Maze maze) {
		this.maze = maze;
		
		layers.clear();
		
		if (maze != null){
			addLayer(maze);
			addLayer(new ChessLayer(maze));
		}
			
		componentResized(null);
	}
	
	public MazePanel() {
		setBackground(Color.black);
		setMaze(null);		
		
		//addMouse(new MousePlayer(maze.getGrid().firstElement()));
		
		addMouseWheelListener(new MouseWheelListener() {
			public void mouseWheelMoved(MouseWheelEvent e) {
				if (autoZoom)
					return;
				
				int ammount = e.getUnitsToScroll();
				double lastZoom = zoom;
				if (ammount < 0)
					zoom *= 1.3;									
				else
					zoom *= (1/1.3);
				
				zoom = Math.min(60, zoom);
				zoom = Math.max(1, zoom);
				if (lastZoom != zoom) {
					updateLayers();
				}
				setPreferredSize(new Dimension((int)(MazePanel.this.maze.getWidth() * zoom), (int)(MazePanel.this.maze.getHeight() * zoom)));
				setSize((int)(MazePanel.this.maze.getWidth() * zoom), (int)(MazePanel.this.maze.getHeight() * zoom));
				repaint();
				
			}
		});
		
		addComponentListener(this);
	}
		
		public void componentResized(ComponentEvent e) {

			if (MazePanel.this.maze == null || !autoZoom || getWidth() == 0)
				return;

			double zoomW = (double)(getWidth()-6) / MazePanel.this.maze.getWidth();
			double zoomH = (double)(getHeight()-6) / MazePanel.this.maze.getHeight();

			MazePanel.this.zoom = Math.min(zoomW, zoomH);
			updateLayers();
			repaint();					
		}

	public Point2D getScreenCoordinates(Point2D worldCoordinates) {
		Point2D.Double ret = new Point2D.Double((getWidth()-maze.getWidth()*zoom)/2.0,getHeight()-(getHeight()-maze.getHeight()*zoom)/2.0);
		ret.x = ret.x + worldCoordinates.getX() * zoom;
		ret.y = ret.y - worldCoordinates.getY() * zoom;
		return ret;
	}
	
	public void paint(Graphics g) {
		super.paint(g);
		
		if (maze == null)
			return;
		
		g.translate((int)((getWidth()-maze.getWidth()*zoom)/2),
				(int)((getHeight()-maze.getHeight()*zoom)/2));
		
		for (BufferedImage bi : layers.values()) {
			g.drawImage(bi, 0, 0, null);
		}
		Graphics2D g2 = (Graphics2D)g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		drawMice(g2);
	}
	
	public void componentHidden(ComponentEvent e) {
		
	}
	
	public void componentMoved(ComponentEvent e) {
		
	}
	
	public void componentShown(ComponentEvent e) {
		
	}
}
